package com.chengke.novel.common.config;


import com.chengke.novel.common.constant.CacheConsts;
import com.github.benmanes.caffeine.cache.Caffeine;

import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;

import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 缓存配置类
 * @Author CK
 * @Date 2023/3/18
 */

@Configuration
public class CacheConfig {

    /**
     * Caffeine 缓存管理器
     * Primary 标记为首选项。
     */
    @Bean
    @Primary
    public CacheManager caffeineCacheManager() {
        // 创建一个 SimpleCacheManager 对象，用于管理多个本地缓存。
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        // 创建一个 ArrayList 对象，用于存储所有的本地缓存对象。
        List<CaffeineCache> caches = new ArrayList<>(CacheConsts.CacheEnum.values().length);
        // 类型推断 var 非常适合 for 循环，JDK 10 引入，JDK 11 改进
        // 遍历 CacheConsts.CacheEnum 枚举类型中的所有缓存名称和参数配置，
        // 如果该缓存是本地缓存，则根据参数创建对应的 CaffeineCache 对象，并将其加入到 caches 中。
        for (var c : CacheConsts.CacheEnum.values()) {
            if (c.isLocal()) {
                Caffeine<Object, Object> caffeine = Caffeine.newBuilder().recordStats()
                        .maximumSize(c.getMaxSize());
                if (c.getTtl() > 0) {
                    caffeine.expireAfterWrite(Duration.ofSeconds(c.getTtl()));
                }
                caches.add(new CaffeineCache(c.getName(), caffeine.build()));
            }
        }

        // 将创建的本地缓存对象列表设置到 SimpleCacheManager 中
        cacheManager.setCaches(caches);
        return cacheManager;
    }

    /**
     * Redis 缓存管理器，
     */
    @Bean
    public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
        // 创建一个 RedisCacheWriter 对象，指定使用非锁定的写入方式。
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(
                connectionFactory);

        // 使用 RedisCacheConfiguration 类创建一个缓存配置对象，并禁用缓存空值，设置缓存名称前缀为 CacheConsts.REDIS_CACHE_PREFIX。
        RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .disableCachingNullValues().prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX);

        // 用于存储缓存名称和 RedisCacheConfiguration 缓存配置对象的映射关系。
        Map<String, RedisCacheConfiguration> cacheMap = new LinkedHashMap<>(
                CacheConsts.CacheEnum.values().length);
        // 类型推断 var 非常适合 for 循环，JDK 10 引入，JDK 11 改进
        // 遍历 CacheConsts.CacheEnum 枚举类型中的所有缓存名称和参数配置，
        // 如果该缓存是远程缓存，则根据参数创建对应的 RedisCacheConfiguration 缓存配置对象，并将其加入到 cacheMap 中。
        for (var c : CacheConsts.CacheEnum.values()) {
            if (c.isRemote()) {
                if (c.getTtl() > 0) {
                    cacheMap.put(c.getName(),
                            RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
                                    .prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX)
                                    .entryTtl(Duration.ofSeconds(c.getTtl())));
                } else {
                    cacheMap.put(c.getName(),
                            RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
                                    .prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX));
                }
            }
        }

        // 传入 RedisCacheWriter 对象、默认的 RedisCacheConfiguration 缓存配置对象和 cacheMap 对象
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,
                defaultCacheConfig, cacheMap);
        // 设置 RedisCacheManager 对象为事务感知模式。
        redisCacheManager.setTransactionAware(true);
        // 初始化 RedisCacheManager 对象中的所有缓存。
        redisCacheManager.initializeCaches();
        return redisCacheManager;
    }
}
